package sk.velkavrana.app.ui.components;

import com.jgoodies.binding.adapter.BasicComponentFactory;
import com.jgoodies.binding.adapter.ComboBoxAdapter;
import com.jgoodies.binding.beans.PropertyAdapter;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import org.apache.commons.lang.StringUtils;
import org.hibernate.dialect.Dialect;
import sk.velkavrana.app.context.Context;
import sk.velkavrana.app.model.ConnectionInfo;
import sk.velkavrana.app.util.DBUtil;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.sql.Connection;
import java.util.List;

/**
 * @author mtymes
 * @since May 19, 2009 11:03:27 AM
 */
public class JConnectionPanel extends JPanel {

    private ConnectionInfo connectionInfo = new ConnectionInfo();

    private JComboBox driverComboBox;
    private JTextField urlTextField;
    private JTextLink urlFormatTextLink;
    private JTextField loginTextField;
    private JPasswordField passwordTextField;
    private JTextField schemaTextField;
    private JComboBox dialectComboBox;


    private Runnable connectionTestRunnable = new Runnable() {
        public void run() {
            String driver = getConnectionInfoEx().getDriver();
            try {
                Class.forName(driver != null ? driver : "");
            } catch (ClassNotFoundException e1) {
                JOptionPane.showMessageDialog(
                        JConnectionPanel.this,
                        "Can't find the driver class:\n" + driver,
                        "Invalid driver",
                        JOptionPane.WARNING_MESSAGE
                );
                return;
            }

            Connection conn = DBUtil.connectSilently(getConnectionInfoEx());
            if (conn == null) {
                JOptionPane.showMessageDialog(
                        JConnectionPanel.this,
                        "Can't connect to database.\nPlease correct your parameters",
                        "Invalid connection parameters",
                        JOptionPane.WARNING_MESSAGE
                );

            } else {

                String schema = connectionInfo.getSchema();
                List<String> schemas = DBUtil.getSchemas(conn);

                DBUtil.closeSilently(conn);

                if (StringUtils.isNotBlank(schema) && !schemas.contains(schema)) {
                    JOptionPane.showMessageDialog(
                            JConnectionPanel.this,
                            "Provided schema \"" + schema + "\" is not present in database",
                            "Invalid schema",
                            JOptionPane.WARNING_MESSAGE
                    );
                } else {

                    String dialect = connectionInfo.getDialectClassName();

                    try {
                        Class dialectClass = Class.forName(dialect);

                        if (!Dialect.class.isAssignableFrom(dialectClass)) {
                            JOptionPane.showMessageDialog(
                                    JConnectionPanel.this,
                                    "Provided dialect class \"" + dialect + "\" doesn't extend class\n" + Dialect.class.getName(),
                                    "Bad dialect class",
                                    JOptionPane.WARNING_MESSAGE
                            );

                            return;
                        }

                    } catch (ClassNotFoundException e) {
                        JOptionPane.showMessageDialog(
                                JConnectionPanel.this,
                                "Provided dialect \"" + dialect + "\" was not found",
                                "Unknown dialect",
                                JOptionPane.WARNING_MESSAGE
                        );

                        return;
                    }

                    JOptionPane.showMessageDialog(
                            JConnectionPanel.this,
                            "Connection parameters are correct",
                            "Connection working",
                            JOptionPane.INFORMATION_MESSAGE
                    );
                }
            }
        }
    };


    private JButton createConnectionTestButton() {
        JButton connectionTestButton = new JButton(new AbstractAction("Test connection") {
            public void actionPerformed(ActionEvent e) {
                // todo: add wait dialog
                Thread connectionTestThread = new Thread(connectionTestRunnable);
                connectionTestThread.start();
            }
        });

//        connectionTestButton.setBorder(BorderFactory.createEtchedBorder());

        return connectionTestButton;
    }


    private void init() {
        FormLayout connectionLayout = new FormLayout(
                "4dlu, right:pref, 4dlu, pref:grow(0.1), 4dlu, pref, 4dlu", // columns
                "2dlu, pref, 1dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu, pref, 4dlu" // rows
        );

        this.setLayout(connectionLayout);
        this.setBorder(BorderFactory.createTitledBorder("Connection info"));

        CellConstraints cc = new CellConstraints();

        int row = 2;
        this.add(new JLabel("Driver:"), cc.xy(2, row));
        ValueModel driverModel = new PropertyAdapter(connectionInfo, "driver");
        ComboBoxAdapter driverComboBoxModel = new ComboBoxAdapter(Context.getInstance().getDatabaseService().getAllJDBCDriverClasses(), driverModel);
        driverComboBox = new JComboBox(driverComboBoxModel);
        driverComboBox.setEditable(true);
        this.add(driverComboBox, cc.xyw(4, row, 3));

        driverComboBox.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent e) {
                urlFormatTextLink.setText(Context.getInstance().getDatabaseService().getUrlFormat((String) driverComboBox.getSelectedItem()));
            }
        });

        row += 2;
//        this.add(new JLabel("format:"), cc.xy(2, row));
        urlFormatTextLink = new JTextLink(new AbstractAction("") {
            public void actionPerformed(ActionEvent e) {
                String urlFormat = urlFormatTextLink.getText();

                urlTextField.setText(urlFormat);
            }
        });
        urlFormatTextLink.setText(Context.getInstance().getDatabaseService().getUrlFormat(connectionInfo.getDriver()));
        this.add(urlFormatTextLink, cc.xyw(4, row, 3));

        row += 2;
        this.add(new JLabel("Url:"), cc.xy(2, row));
        ValueModel urlModel = new PropertyAdapter(connectionInfo, "url");
        urlTextField = BasicComponentFactory.createTextField(urlModel);
        this.add(urlTextField, cc.xyw(4, row, 3));

        row += 2;
        this.add(new JLabel("Login:"), cc.xy(2, row));
        ValueModel loginModel = new PropertyAdapter(connectionInfo, "login");
        loginTextField = BasicComponentFactory.createTextField(loginModel);
        this.add(loginTextField, cc.xy(4, row));


        JButton connectionTestButton = createConnectionTestButton();
        this.add(connectionTestButton, cc.xywh(6, row, 1, 3));

        row += 2;
        this.add(new JLabel("Password:"), cc.xy(2, row));
        ValueModel passwordModel = new PropertyAdapter(connectionInfo, "password");
        passwordTextField = BasicComponentFactory.createPasswordField(passwordModel);
        this.add(passwordTextField, cc.xy(4, row));

        row += 2;
        this.add(new JLabel("Schema:"), cc.xy(2, row));
        ValueModel schemaModel = new PropertyAdapter(connectionInfo, "schema");
        schemaTextField = BasicComponentFactory.createTextField(schemaModel);
        this.add(schemaTextField, cc.xyw(4, row, 3));

        row += 2;
        this.add(new JLabel("Dialect:"), cc.xy(2, row));
        ValueModel dialectModel = new PropertyAdapter(connectionInfo, "dialectClassName");
        ComboBoxAdapter dialectComboBoxModel = new ComboBoxAdapter(Context.getInstance().getDatabaseService().getAllDialects(), dialectModel);
        dialectComboBox = new JComboBox(dialectComboBoxModel);
        dialectComboBox.setEditable(true);
        this.add(dialectComboBox, cc.xyw(4, row, 3));
    }


    public JConnectionPanel(LayoutManager layout, boolean isDoubleBuffered) {
        super(layout, isDoubleBuffered);

        init();
    }


    public JConnectionPanel(LayoutManager layout) {
        super(layout);

        init();
    }


    public JConnectionPanel(boolean isDoubleBuffered) {
        super(isDoubleBuffered);

        init();
    }


    public JConnectionPanel() {
        init();
    }


    /**
     * @return copy of ConnectionInfoEx object
     */
    public ConnectionInfo getConnectionInfoEx() {
        return new ConnectionInfo(connectionInfo);
    }


    public void setConnectionInfoEx(ConnectionInfo connectionInfo) {
        driverComboBox.getEditor().setItem(connectionInfo.getDriver());
        urlTextField.setText(connectionInfo.getUrl());
        loginTextField.setText(connectionInfo.getLogin());
        passwordTextField.setText(connectionInfo.getPassword());
        schemaTextField.setText(connectionInfo.getSchema());
//        dialectTextField.setText(connectionInfo.getDialectClassName());
        dialectComboBox.getEditor().setItem(connectionInfo.getDialectClassName());
        urlFormatTextLink.setText(Context.getInstance().getDatabaseService().getUrlFormat(connectionInfo.getDriver()));

        // todo: for some reasons this is needed - find how to remove it
        this.connectionInfo.setDriver(connectionInfo.getDriver());
        this.connectionInfo.setUrl(connectionInfo.getUrl());
        this.connectionInfo.setLogin(connectionInfo.getLogin());
        this.connectionInfo.setPassword(connectionInfo.getPassword());
        this.connectionInfo.setSchema(connectionInfo.getSchema());
        this.connectionInfo.setDialectClassName(connectionInfo.getDialectClassName());

    }
}
